﻿using ORM.DB;
using ORM.Enum;
using ORM.Model;
using ORM.Util;
using System;
using System.Collections.Generic;
using System.Data.Common;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace ORM.Base
{
    public class PreCommand
    {
        public bool TraceSQL { set; get; }
        private SQLBuilder sqlbuilder = new SQLBuilder();
        public PreCommand()
        {
            TraceSQL = false;
        }
        public PreCommand(bool TraceSQL)
        {
            this.TraceSQL = TraceSQL;
        }

        public Command InitInsert<T>(T Entity, string ConnectStringName) where T : BaseEntity
        {
            string sql = sqlbuilder.BuildCommonSQL<T>(Entity, SQLType.INSERT);
            List<Param> ParamDic = new List<Param>();
            Type tp = typeof(T);
            MapperEntity map = MapperBuilder.GetInstance().GetMap(tp);
            PropertyInfo[] infos = map.MapPropertyList;
            foreach (PropertyInfo info in infos)
            {
                Param param = new Param();
                param.ParamName = info.Name;
                param.ParamValue = map.EmitDic[info.Name].EmitGetter(Entity);
                ParamDic.Add(param);
            }
            Command command = DBFactory.CreateCommand(this.TraceSQL);
            command.CommandType = CommandOptionType.EXECUTE;
            command.SQL = sql;
            command.Params = ParamDic;
            command.ConnectStringName = ConnectStringName;
            return command;
        }

        public Command InitRemove<T>(T Entity, string ConnectStringName) where T : BaseEntity
        {
            string sql = sqlbuilder.BuildCommonSQL<T>(Entity, SQLType.REMOVE);
            List<Param> ParamDic = new List<Param>();
            Type tp = typeof(T);
            MapperEntity map = MapperBuilder.GetInstance().GetMap(tp);
            string keyfieldname = map.KeyName;
            PropertyInfo[] infos = map.MapPropertyList;
            foreach (PropertyInfo info in infos)
            {
                if (info.Name == keyfieldname)
                {
                    Param param = new Param();
                    param.ParamName = info.Name;
                    param.ParamValue = map.EmitDic[info.Name].EmitGetter(Entity);
                    ParamDic.Add(param);
                }

            }
            Command command = DBFactory.CreateCommand(this.TraceSQL);
            command.CommandType = CommandOptionType.EXECUTE;
            command.SQL = sql;
            command.Params = ParamDic;
            command.ConnectStringName = ConnectStringName;
            return command;
        }

        public Command InitSave<T>(T Entity, string ConnectStringName) where T : BaseEntity
        {
            string sql = sqlbuilder.BuildCommonSQL<T>(Entity, SQLType.SAVE);
            List<Param> ParamDic = new List<Param>();
            Type tp = typeof(T);
            MapperEntity map = MapperBuilder.GetInstance().GetMap(tp);
            PropertyInfo[] infos = map.MapPropertyList;
            foreach (PropertyInfo info in infos)
            {
                Param param = new Param();
                param.ParamName = info.Name;
                param.ParamValue = map.EmitDic[info.Name].EmitGetter(Entity);
                ParamDic.Add(param);
            }
            Command command = DBFactory.CreateCommand(this.TraceSQL);
            command.CommandType = CommandOptionType.EXECUTE;
            command.SQL = sql;
            command.Params = ParamDic;
            command.ConnectStringName = ConnectStringName;
            return command;
        }

        public Command InitSave<T>(T Entity, string[] SaveColumn, string ConnectStringName) where T : BaseEntity
        {
            string sql = sqlbuilder.BuildSaveSQLWithColumns<T>(Entity, SaveColumn);
            List<Param> ParamDic = new List<Param>();
            Type tp = typeof(T);
            MapperEntity map = MapperBuilder.GetInstance().GetMap(tp);
            PropertyInfo[] infos = map.MapPropertyList;
            foreach (string columnname in SaveColumn)
            {
                Param param = new Param();
                param.ParamName = columnname;
                param.ParamValue = map.EmitDic[columnname].EmitGetter(Entity);
                ParamDic.Add(param);
            }
            Command command = DBFactory.CreateCommand(this.TraceSQL);
            command.CommandType = CommandOptionType.EXECUTE;
            command.SQL = sql;
            command.Params = ParamDic;
            command.ConnectStringName = ConnectStringName;
            return command;
        }

        public Command InitCount<T>(string ConnectStringName) where T : BaseEntity
        {
            string sql = sqlbuilder.BuildCountSQL<T>();
            Command command = DBFactory.CreateCommand(this.TraceSQL);
            command.CommandType = CommandOptionType.QUERY;
            command.SQL = sql;
            command.ConnectStringName = ConnectStringName;
            return command;
        }

        public Command InitFindList<T>(string SQL, string ConnectStringName) where T : BaseEntity
        {
            Command command = DBFactory.CreateCommand(this.TraceSQL);
            command.CommandType = CommandOptionType.QUERY;
            command.SQL = SQL;
            command.ConnectStringName = ConnectStringName;
            return command;
        }

        public Command InitFindView(string SQL, string ConnectStringName)
        {
            Command command = DBFactory.CreateCommand(this.TraceSQL);
            command.CommandType = CommandOptionType.QUERY;
            command.SQL = SQL;
            command.ConnectStringName = ConnectStringName;
            return command;
        }

        public Command InitFindOne<T>(string KeyGuid, string ConnectStringName) where T : BaseEntity
        {
            string sql = sqlbuilder.BuildFindOneSQL<T>();
            List<Param> ParamDic = new List<Param>();
            Type tp = typeof(T);
            MapperEntity map = MapperBuilder.GetInstance().GetMap(tp);
            string keyfieldname = map.KeyName;
            PropertyInfo[] infos = map.PropertyList;
            foreach (PropertyInfo info in infos)
            {
                if (info.Name == keyfieldname)
                {
                    Param param = new Param();
                    param.ParamName = info.Name;
                    param.ParamValue = KeyGuid;
                    ParamDic.Add(param);
                }

            }
            Command command = DBFactory.CreateCommand(this.TraceSQL);
            command.CommandType = CommandOptionType.QUERY;
            command.SQL = sql;
            command.Params = ParamDic;
            command.ConnectStringName = ConnectStringName;
            return command;
        }

        public Command InitFindPage<T>(string SQL, int PageSize, int PageNum, string OrderBy, string ConnectStringName) where T : BaseEntity
        {
            if (!string.IsNullOrEmpty(OrderBy))
            {
                SQL += " order by " + OrderBy;
            }
            SQL += " limit " + (PageNum - 1) * PageSize + "," + PageSize;
            return InitFindList<T>(SQL, ConnectStringName);
        }

        public Command InitFindViewPage(string SQL, int PageSize, int PageNum, string OrderBy, string ConnectStringName)
        {
            if (!string.IsNullOrEmpty(OrderBy))
            {
                SQL += " order by " + OrderBy;
            }
            SQL += " limit " + (PageNum - 1) * PageSize + "," + PageSize;
            return InitFindView(SQL, ConnectStringName);
        }

        public Command InitExecuteSQL(string SQL, string ConnectStringName)
        {
            Command command = DBFactory.CreateCommand(this.TraceSQL);
            command.CommandType = CommandOptionType.EXECUTE;
            command.SQL = SQL;
            command.ConnectStringName = ConnectStringName;
            return command;
        }

        public bool InsertBatch<T>(List<T> EntityList, string ConnectStringName, int BatchSize = 500) where T : BaseEntity
        {
            DBHelper _DBHelper = new DBHelper(ConnectStringName);
            int insertgroupnum = 0;
            //商
            int groupnum = EntityList.Count / BatchSize;
            //余数
            int grouprest = EntityList.Count - BatchSize * groupnum;
            if (grouprest != 0)
            {
                groupnum++;
            }
            Type tp = typeof(T);
            PropertyInfo[] infos = tp.GetProperties();
            for (int i = 0; i < groupnum; i++)
            {
                using (DbConnection connection = _DBHelper.GetConnection())
                {
                    connection.Open();
                    DbTransaction transaction = connection.BeginTransaction();
                    try
                    {
                        for (int j = i * BatchSize; j < (i + 1) * BatchSize && j < EntityList.Count; j++)
                        {
                            Command command = InitInsert<T>(EntityList[j], ConnectStringName);
                            command.Transition = transaction;
                            command.Execute();
                        }
                        transaction.Commit();
                        insertgroupnum++;

                    }
                    catch (System.Exception e)
                    {
                        transaction.Rollback();
                        throw e;
                    }
                    finally
                    {
                        connection.Close();
                    }
                }

            }
            if (insertgroupnum == groupnum)
            {
                return true;
            }
            else
            {
                return false;
            }
        }


    }

}
